﻿
#If Not PocketPC AndAlso Not Smartphone Then
Imports std

Public Class scriptmonitor(Of T As scriptapp)
    Public Event scriptChanged()
    Public Shared Event scriptChanged2(ByVal scriptfile As String)
    Public Const retrytimes As Int64 = 64
    Private _app As T = Nothing
    Private scriptfile As String = Nothing
    Private WithEvents watcher As saferFileSystemWatcher = Nothing
    Private parser As scriptparser = Nothing

    Public Function app() As T
        Return _app
    End Function

    Public Shared Operator +(ByVal this As scriptmonitor(Of T)) As T
        If this Is Nothing Then
            Return Nothing
        Else
            Return this.app()
        End If
    End Operator

    Public Function valid() As Boolean
        Return Not (+Me) Is Nothing
    End Function

    Public Sub New(ByVal scriptfile As String, ByVal parser As scriptparser)
        copy(Me.scriptfile, scriptfile)
        Me.parser = parser

        watcher = New saferFileSystemWatcher()
        watcher.NotifyFilter = IO.NotifyFilters.LastWrite Or IO.NotifyFilters.FileName
        Try
            watcher.Path = parentpath(scriptfile)
            watcher.Filter = filename(scriptfile)
            watcher.EnableRaisingEvents = True
        Catch
            watcher_failmonitor()
        End Try
        parseImpl()
    End Sub

    Private Function parseImpl(Optional ByVal needmsg As Boolean = True) As Boolean
        Dim app As T = Nothing
        If parser.parse(scriptfile, app) Then
            _app = app
            Return True
        Else
            If needmsg Then
                raiseError("cannot parse script file " + scriptfile, errorHandle.errorType.user)
            End If
            Return False
        End If
    End Function

    Private Sub watcher_changes() Handles watcher.changes
        Dim i As Int64
        For i = 0 To retrytimes - 2
            If parseImpl(False) Then
                Exit For
            End If
        Next
        If i < retrytimes - 1 OrElse parseImpl() Then
            RaiseEvent scriptChanged()
            RaiseEvent scriptChanged2(scriptfile)
        End If
    End Sub

    Private Sub watcher_failmonitor() Handles watcher.failmonitor
        raiseError("cannot monitor script file " + scriptfile, errorHandle.errorType.exclamation)
    End Sub

    'two ways to use this class,
    '1. override parameters and leave invokeImpl
    '2. override invokeImpl directly, but not recommended
    Public MustInherit Class invokeDelegate
        Private app As scriptmonitor(Of T) = Nothing

        Public MustOverride Function functionName() As String

        Public Function invoke(ByRef rtn As bare_variable) As Boolean
            Return Not app Is Nothing AndAlso app.valid() AndAlso invokeImpl(+app, rtn)
        End Function

        Protected Overridable Function parameters() As iParameters
            Return Nothing
        End Function

        Protected Overridable Function invokeImpl(ByVal app As T, ByRef rtn As bare_variable) As Boolean
            Return app.invoke(functionName(), parameters(), rtn)
        End Function

        Public Sub New(ByVal app As scriptmonitor(Of T))
            Me.app = app
        End Sub

        Public Function scriptfile() As String
            If app Is Nothing Then
                Return "#NO_VALID_APP#"
            Else
                Return app.scriptfile
            End If
        End Function
    End Class

    Public Shared Function invoke(ByVal dele As invokeDelegate, _
                                  Optional ByRef rtn As bare_variable = Nothing) As Boolean
        If dele Is Nothing Then
            Return False
        ElseIf Not dele.invoke(rtn) Then
            raiseError("cannot invoke " + dele.functionName() + " funciton in " + dele.scriptfile(), _
                       errorHandle.errorType.user)
            Return False
        Else
            Return True
        End If
    End Function

    Public Shared Function invoke(ByVal app As scriptmonitor(Of T), _
                                  ByVal functionname As String, _
                                  ByRef rtn As bare_variable, _
                                  Optional ByVal parameters As vector(Of pair(Of String, bare_variable)) = Nothing) _
                                 As Boolean
        If app Is Nothing OrElse Not app.valid() Then
            Return False
        ElseIf Not (+app).invoke(functionname, parameters, rtn) Then
            raiseError("cannot invoke " + functionname + " funciton.", errorHandle.errorType.user)
            Return False
        Else
            Return True
        End If
    End Function

    Protected Overrides Sub Finalize()
        If Not watcher Is Nothing Then
            watcher.EnableRaisingEvents = False
            watcher.Dispose()
            watcher = Nothing
        End If
        MyBase.Finalize()
    End Sub
End Class

Public Class scriptparser
    Public Delegate Function parseDelegate(ByVal scriptfile As String, ByRef output As scriptapp) As Boolean
    Public parse As parseDelegate = Nothing

    Public Sub New(ByVal parse As parseDelegate)
        assert(Not parse Is Nothing, "should not initialize without parse.")
        Me.parse = parse
    End Sub
End Class
#End If
